msg_tool\scripts\silky/
map.rs1use crate::ext::io::*;
2use crate::scripts::base::*;
3use crate::types::*;
4use crate::utils::encoding::*;
5use anyhow::Result;
6use std::io::{Seek, SeekFrom};
7
8#[derive(Debug)]
9pub struct MapBuilder {}
11
12impl MapBuilder {
13 pub fn new() -> Self {
15 Self {}
16 }
17}
18
19impl ScriptBuilder for MapBuilder {
20 fn default_encoding(&self) -> Encoding {
21 Encoding::Utf16LE
22 }
23
24 fn build_script(
25 &self,
26 buf: Vec<u8>,
27 _filename: &str,
28 encoding: Encoding,
29 _archive_encoding: Encoding,
30 config: &ExtraConfig,
31 _archive: Option<&Box<dyn Script>>,
32 ) -> Result<Box<dyn Script>> {
33 Ok(Box::new(Map::new(buf, encoding, config)?))
34 }
35
36 fn extensions(&self) -> &'static [&'static str] {
37 &["map"]
38 }
39
40 fn script_type(&self) -> &'static ScriptType {
41 &ScriptType::SilkyMap
42 }
43
44 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
45 let reader = MemReaderRef::new(&buf[..buf_len]);
46 try_parse(reader).ok()
47 }
48
49 fn can_create_file(&self) -> bool {
50 true
51 }
52
53 fn create_file<'a>(
54 &'a self,
55 filename: &'a str,
56 writer: Box<dyn WriteSeek + 'a>,
57 encoding: Encoding,
58 file_encoding: Encoding,
59 config: &ExtraConfig,
60 ) -> Result<()> {
61 create_file(
62 filename,
63 writer,
64 encoding,
65 file_encoding,
66 config.custom_yaml,
67 )
68 }
69}
70
71fn create_file<'a>(
72 custom_filename: &'a str,
73 mut writer: Box<dyn WriteSeek + 'a>,
74 encoding: Encoding,
75 output_encoding: Encoding,
76 yaml: bool,
77) -> Result<()> {
78 let input = crate::utils::files::read_file(custom_filename)?;
79 let s = decode_to_string(output_encoding, &input, true)?;
80 let strings: Vec<String> = if yaml {
81 serde_yaml_ng::from_str(&s)?
82 } else {
83 serde_json::from_str(&s)?
84 };
85 writer.write_u32(strings.len() as u32)?;
86 let header_len = 8 * strings.len();
87 writer.seek_relative(header_len as i64)?;
88 let mut offsets = Vec::with_capacity(strings.len());
89 for s in strings {
90 offsets.push(writer.stream_position()? as u32);
91 let buf = if encoding.is_utf16le() {
92 let mut buf = encode_string(encoding, &s, false)?;
93 buf.extend_from_slice(&[0, 0]);
94 buf
95 } else {
96 let mut buf = encode_string(encoding, &s, false)?;
97 buf.push(0);
98 buf
99 };
100 writer.write_all(&buf)?;
101 }
102 writer.seek(SeekFrom::Start(4))?;
103 for (i, offset) in offsets.iter().enumerate() {
104 writer.write_u32(i as u32)?;
105 writer.write_u32(*offset)?;
106 }
107 Ok(())
108}
109
110#[derive(Debug)]
111struct Map {
113 strings: Vec<String>,
114 custom_yaml: bool,
115}
116
117impl Map {
118 pub fn new(buf: Vec<u8>, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
120 let mut data = MemReader::new(buf);
121 let count = data.read_u32()?;
122 let mut strings = Vec::with_capacity(count as usize);
123 for _ in 0..count {
124 let _index = data.read_u32()?;
125 let offset = data.read_u32()?;
126 if encoding.is_utf16le() {
127 let data = data.peek_u16string_at(offset as u64)?;
128 let s = decode_to_string(encoding, &data, true)?;
129 strings.push(s);
130 } else {
131 let data = data.peek_cstring_at(offset as u64)?;
132 let s = decode_to_string(encoding, data.as_bytes(), true)?;
133 strings.push(s);
134 }
135 }
136 Ok(Self {
137 strings,
138 custom_yaml: config.custom_yaml,
139 })
140 }
141}
142
143impl Script for Map {
144 fn default_output_script_type(&self) -> OutputScriptType {
145 OutputScriptType::Json
146 }
147
148 fn is_output_supported(&self, _: OutputScriptType) -> bool {
149 true
150 }
151
152 fn default_format_type(&self) -> FormatOptions {
153 FormatOptions::None
154 }
155
156 fn custom_output_extension<'a>(&'a self) -> &'a str {
157 if self.custom_yaml { "yaml" } else { "json" }
158 }
159
160 fn extract_messages(&self) -> Result<Vec<Message>> {
161 let mut messages = Vec::with_capacity(self.strings.len());
162 for s in &self.strings {
163 messages.push(Message::new(s.replace("\\n", "\n"), None));
164 }
165 Ok(messages)
166 }
167
168 fn import_messages<'a>(
169 &'a self,
170 messages: Vec<Message>,
171 mut file: Box<dyn WriteSeek + 'a>,
172 _filename: &str,
173 encoding: Encoding,
174 replacement: Option<&'a ReplacementTable>,
175 ) -> Result<()> {
176 if messages.len() != self.strings.len() {
177 return Err(anyhow::anyhow!(
178 "The number of messages does not match. (expected {}, got {})",
179 self.strings.len(),
180 messages.len()
181 ));
182 }
183 file.write_u32(messages.len() as u32)?;
184 let header_len = 8 * messages.len();
185 file.seek_relative(header_len as i64)?;
186 let mut offsets = Vec::with_capacity(messages.len());
187 for msg in messages {
188 let mut m = msg.message.clone();
189 if let Some(table) = replacement {
190 for (k, v) in &table.map {
191 m = m.replace(k, v);
192 }
193 }
194 m = m.replace("\n", "\\n");
195 offsets.push(file.stream_position()? as u32);
196 let buf = if encoding.is_utf16le() {
197 let mut buf = encode_string(encoding, &m, false)?;
198 buf.extend_from_slice(&[0, 0]);
199 buf
200 } else {
201 let mut buf = encode_string(encoding, &m, false)?;
202 buf.push(0);
203 buf
204 };
205 file.write_all(&buf)?;
206 }
207 file.seek(SeekFrom::Start(4))?;
208 for (i, offset) in offsets.iter().enumerate() {
209 file.write_u32(i as u32)?;
210 file.write_u32(*offset)?;
211 }
212 Ok(())
213 }
214
215 fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
216 let s = if self.custom_yaml {
217 serde_yaml_ng::to_string(&self.strings)?
218 } else {
219 serde_json::to_string_pretty(&self.strings)?
220 };
221 let s = encode_string(encoding, &s, false)?;
222 let mut file = crate::utils::files::write_file(filename)?;
223 file.write_all(&s)?;
224 Ok(())
225 }
226
227 fn custom_import<'a>(
228 &'a self,
229 custom_filename: &'a str,
230 file: Box<dyn WriteSeek + 'a>,
231 encoding: Encoding,
232 output_encoding: Encoding,
233 ) -> Result<()> {
234 create_file(
235 custom_filename,
236 file,
237 encoding,
238 output_encoding,
239 self.custom_yaml,
240 )
241 }
242}
243
244fn try_parse(mut r: MemReaderRef) -> Result<u8> {
245 let count = r.read_u32()?;
246 let index = r.read_u32()?;
247 if index != 0 {
248 return Err(anyhow::anyhow!("Invalid index"));
249 }
250 let mut prv_offset = r.read_u32()?;
251 if prv_offset < 4 + 8 * count {
252 return Err(anyhow::anyhow!("Invalid offset"));
253 }
254 let tlen = r.data.len();
255 for i in 1..count {
256 if r.pos + 8 > tlen {
257 break;
258 }
259 let index = r.read_u32()?;
260 if index != i {
261 return Err(anyhow::anyhow!("Invalid index"));
262 }
263 let offset = r.read_u32()?;
264 if offset <= prv_offset {
265 return Err(anyhow::anyhow!("Invalid offset"));
266 }
267 prv_offset = offset;
268 }
269 Ok(if (r.pos - 4) / 8 < 100 { 10 } else { 20 })
270}